export class GraphNode {
    id: string = "";
    name: string = "";
    attrs: any;
    incomingEdges: GraphEdge[] = [];
    outgoingEdges: GraphEdge[] = [];
    inDegree(): number {
        return this.incomingEdges.length;
    }
    outDegree(): number {
        return this.outgoingEdges.length;
    }
    constructor(id: string, name: string, attrs: any, incomingEdges: GraphEdge[], outgoingEdges: GraphEdge[]) {
        this.id = id;
        this.name = name;
        this.attrs = attrs;
        this.incomingEdges = incomingEdges;
        this.outgoingEdges = outgoingEdges;
    }
}

export class GraphEdge {
    source: GraphNode;
    target: GraphNode;
    id: string = "";
    name: string = "";
    attrs: any;
    sourceId(): string {
        return this.source.id;
    }
    targetId(): string {
        return this.target.id;
    }
    constructor(source: GraphNode, target: GraphNode) {
        this.source = source;
        this.target = target;
        this.id = `${source.id}->${target.id}`;
    }
}

export class Graph {
    nodes: GraphNode[];
    edges: GraphEdge[];
    constructor() {
        this.nodes = [];
        this.edges = [];
    }
    addNode(node: GraphNode) {
        this.nodes.push(node);
    }
    addEdge(edge: GraphEdge) {
        this.edges.push(edge);
    }
}

export class VisualGraphNode extends GraphNode {
    x: number;
    y: number;
    level: number;
    directPredList: VisualGraphNode[] = [];
    directSuccList: VisualGraphNode[] = [];
    directPred(): VisualGraphNode {
        return this.directPredList[0];
    }
    constructor(graphNode: GraphNode, x: number, y: number, level: number) {
        super(graphNode.id, graphNode.name, graphNode.attrs, graphNode.incomingEdges, graphNode.outgoingEdges);
        this.x = x;
        this.y = y;
        this.level = level;
    }
}

export class VisualGraphEdge extends GraphEdge {
    source: VisualGraphNode;
    target: VisualGraphNode;
    constructor(source: VisualGraphNode, target: VisualGraphNode) {
        super(source, target);
        this.source = source;
        this.target = target;
    }
}

export class VisualGraph {
    nodes: VisualGraphNode[];
    edges: VisualGraphEdge[];
    root: VisualGraphNode;
    levelMap: Map<number, VisualGraphNode[]>;
    minX(): number {
        return Math.min(...this.nodes.map((node) => node.x));
    }
    minY(): number {
        return Math.min(...this.nodes.map((node) => node.y));
    }
    maxX(): number {
        return Math.max(...this.nodes.map((node) => node.x));
    }
    maxY(): number {
        return Math.max(...this.nodes.map((node) => node.y));
    }
    constructor(nodes: VisualGraphNode[], edges: VisualGraphEdge[], root: VisualGraphNode, levelMap: Map<number, VisualGraphNode[]>) {
        this.nodes = nodes;
        this.edges = edges;
        this.root = root;
        this.levelMap = levelMap;
    }
    static blank(): VisualGraph {
        return new VisualGraph([], [], new VisualGraphNode(new GraphNode("", "", {}, [], []), 0, 0, 0), new Map());
    }
}